home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#49 (Oct 89)
/
DMP Source
/
src
/
pdef0.c
< prev
next >
Wrap
Text File
|
1989-01-02
|
19KB
|
611 lines
/*
* Draft mode, text-only printing code for the Macintosh.
*/
/*
* This file is part of the DMP-110 printer driver for the Macintosh
* series of computers.
*/
/*
* Earle R. Horton.
* Wednesday, November 30, 1988
* All rights reserved.
*/
#include <Windows.h>
#include <Events.h>
#include <Dialogs.h>
#include <Fonts.h>
#include <Memory.h>
#include <Resources.h>
#include <ToolUtils.h>
#include <Errors.h>
#include <OSUtils.h>
#include <Desk.h>
#include "dmp-110.h"
#include "compat.h"
#define __SEG__ Main
/*
* Use our own names for these.
*/
#define storage lGParam1
#define pagenum lGParam2
#define PREC lGParam3
#define picture lGParam4
/* Useful constants */
#define SERRESET 8
#define SERSHAKE 10
#define XONCR ((char)17)
#define XOFFCR ((char)19)
#define RESFILEID (-8192)
OSErr SPOpen();
#define WNETrapNumber 0x60
#define UnImpTrapNumber 0x9f
/*
* Abort printing if command '.' pressed.
*/
checkabort()
{
EventRecord myevent;
Boolean result;
Boolean WNEisThere = NGetTrapAddress(WNETrapNumber,1) !=
NGetTrapAddress(UnImpTrapNumber,1);
if(WNEisThere){
result = WaitNextEvent(everyEvent,&myevent,0L,0L);
}else{
SystemTask();
result = GetNextEvent(keyDownMask|autoKeyMask, &myevent);
}
if(result){
if(LoWord(myevent.message & charCodeMask) == '.' &&
(myevent.modifiers & cmdKey) ){
PrSetError(iIOAbortErr); /* iPrAbort? */
}
}
}
/*
* This function returns a pointer to a specialized GrafPort
* (a TPrPort) customized for printing.
*
* Based on the passed Print Record, we prepare a GrafPort for use
* by the application in printing. We store information of use to
* us in the lGParam[n] fields of the TPrPort. We use a BitMap
* whose baseAddr contains a pointer to a buffer whose size is
* taken from the prXInfo field of the print record. We define the
* portRect to correspond to the prInfo.rPage field of the print
* record. Later, we will scale the application's drawing commands
* to fit into the prInfoPT.rPage Rect, and print the Picture which
* the application has created.
*/
pascal TPPrPort myPrOpenDoc(hPrint,pPrPort,pIOBuf)
THPrint hPrint;
TPPrPort pPrPort;
Ptr pIOBuf;
{
TPPrPort thisport;
Handle us;
THPrint savePrint;
BitMap mybits;
THPrint ourPrint;
DCtlHandle d;
DPstorage s;
SysEnvRec World;
short refnum;
/*
* Allocate storage, abort if no space for TPrPort.
* First stage aborts if not enough for a TPrPort, setting
* PrError() to memFullErr and returning nil.
*/
if(pPrPort == nil){
if((thisport = (TPPrPort)
NewPtr((Size)sizeof(TPrPort))) == nil){
PurgeMem(maxSize);
thisport = (TPPrPort)NewPtr((Size)sizeof(TPrPort));
if(thisport == nil){
PrSetError(memFullErr);
return nil;
}
}
thisport->fOurPtr = TRUE; /* TRUE: Belongs to printing code. */
}
else {
thisport = pPrPort;
thisport->fOurPtr = FALSE; /* FALSE: Belongs to application. */
}
thisport->fOurBits = TRUE;
/*
* If we get here, there is enough space for at least the TPrPort.
*
* The next block of code attempts to allocate all the dynamic storage
* we will need during printing. If it fails, it sets thisport->storage
* to nil, and sets PrError() to memFullErr. The application should detect
* the print error condition, and call PrCloseDoc. Dynamic storage which
* was not allocated successfully is disposed of by PrCloseDoc. Storage
* is allocated in several blocks rather than in a single big block in
* order to facilitate changing the size of the output buffer or band.
*/
thisport->storage = (long)(s = (DPstorage)NewPtr((Size)sizeof(Dstorage)));
if(s != nil){
memset(s,0,sizeof(Dstorage));
s->obuf = NewPtr((Size)2000);
}
ourPrint = hPrint;
if(HandToHand(&ourPrint) == noErr){
thisport->PREC = (long)ourPrint;
}else{
thisport->PREC = nil;
}
thisport->gPort.portBits.baseAddr = mybits.baseAddr = NewPtr((Size)
((*hPrint)->prXInfo.iRowBytes*(*hPrint)->prXInfo.iBandV));
if( (thisport->storage == nil) ||
(s->obuf == nil) ||
(thisport->PREC == nil) ||
(mybits.baseAddr == nil) ){
PrSetError(memFullErr);
}else{
/*
* If we get here, then memory worries are over, except of course if we
* run out while defining a picture. Don't know what happens then.
* The TPrPort is ready to be initialized, and the serial port to be
* opened. Only possible fatal error remaining is a serial port error.
*/
OpenPort(thisport);
SetStdProcs(&thisport->gProcs); /* Fill out gProcs for this port. */
thisport->gPort.grafProcs = &thisport->gProcs;
SetPort(thisport);
SysEnvirons(1,&World);
PrSetError(SPOpen(s,&World));
/*
* Save our copy of Print Record Handle, then open the port.
*/
thisport->PREC = (long)ourPrint;
/*
* Define the port's BitMap as our printing band.
*
*/
mybits.bounds.top = 0;
mybits.bounds.left = 0;
mybits.bounds.bottom = (*hPrint)->prXInfo.iBandV;
mybits.bounds.right = (*hPrint)->prXInfo.iBandH+1;
mybits.rowBytes = (*hPrint)->prXInfo.iRowBytes;
SetPortBits(&mybits);
MovePortTo((*hPrint)->prInfo.rPage.left,(*hPrint)->prInfo.rPage.top);
PortSize((*hPrint)->prInfo.rPage.right-(*hPrint)->prInfo.rPage.left,
(*hPrint)->prInfo.rPage.bottom-(*hPrint)->prInfo.rPage.top);
RectRgn(thisport->gPort.visRgn,&thisport->gPort.portRect);
RectRgn(thisport->gPort.clipRgn,&thisport->gPort.portRect);
thisport->pagenum = 1;
thisport->storage = (long)s;
if((*ourPrint)->prJob.pIdleProc == nil)
(*ourPrint)->prJob.pIdleProc = (ProcPtr)checkabort;
/*
* Attempt to save Print Record in 'PREC' 1 in the printer file.
* No big deal if failure, since this is not essential to printing.
*/
savePrint = (THPrint)GetResource('PREC',1);
if(savePrint != nil){
LoadResource(savePrint);
**savePrint = **ourPrint;
ChangedResource(savePrint);
UpdateResFile(HomeResFile(savePrint));
}
}
return thisport;
}
/*
* Called at completion of a print job, whether successful or not.
* Clean up any dynamic storage which requires it, then set print
* error to noErr.
*/
pascal void myPrCloseDoc(pPrPort)
TPPrPort pPrPort;
{
DPstorage s;
if( (pPrPort != nil) &&
((s = (DPstorage)pPrPort->storage) != nil) &&
(s->obuf != nil) &&
(pPrPort->PREC != nil) &&
(pPrPort->gPort.portBits.baseAddr != nil) ) { /* Assume successful open. */
ClosePort(pPrPort);
PBClose(&s->iopb,FALSE);
}
if(pPrPort != nil){ /* Dispose of storage which needs it. */
if(s != nil){
if(s->obuf != nil){
DisposPtr(s->obuf);
}
DisposPtr(s);
}
if(pPrPort->PREC != nil){
DisposHandle(pPrPort->PREC);
}
if(pPrPort->gPort.portBits.baseAddr != nil){
DisposPtr(pPrPort->gPort.portBits.baseAddr);
}
if(pPrPort->fOurPtr){
DisposPtr(pPrPort);
}
}
PrSetError(noErr); /* No bad feelings! */
}
/*
* This routine opens a new page. Check for valid TPrPort, then
* just start a new picture definition.
*/
pascal void myPrOpenPage(pPrPort,pPageFrame)
TPPrPort pPrPort;
TPRect pPageFrame;
{
Rect *scalerect;
if(pPageFrame != nil){
scalerect = pPageFrame;
}else{
scalerect = &pPrPort->gPort.portRect;
}
SetPort(pPrPort);
/*
* The application may run out of memory while the picture is being
* saved. Don't know how to prevent or even detect this from here.
*/
pPrPort->picture = (long)OpenPicture(scalerect);
}
/*
* This is the routine which does the actual printing.
* Close the picture for the page. Use DrawPicture() to draw repeatedly,
* while moving the BitMap down the page to capture an image for each
* band.
*
* Send each band to the output conversion routines.
*/
pascal void myPrClosePage(pPrPort)
TPPrPort pPrPort;
{
PicHandle thepic;
short i,iocount,vband,nbands,rows,nleft;
Rect savepr,scaleRect,savebm;
long width,height;
THPrint ourPrint;
char formfeed;
DPstorage s;
ourPrint = (THPrint)pPrPort->PREC; /* Set up variables. */
SetPort(pPrPort);
ClosePicture();
thepic = (PicHandle)pPrPort->picture;
vband = (*ourPrint)->prXInfo.iBandV;
nbands = (*ourPrint)->prXInfo.iBands;
s = (DPstorage)pPrPort->storage;
if(PrError() != noErr){
}
else if(setjmp(s->abortbuf) != 0){ /* Pops back here on abort or error. */
PrSetError(iIOAbortErr);
}
else if(pPrPort->pagenum < (*ourPrint)->prJob.iFstPage){/* Check page no. */
pPrPort->pagenum++;
}
else if(pPrPort->pagenum++ > (*ourPrint)->prJob.iLstPage){
PrSetError(iIOAbortErr);
} else {
savepr = pPrPort->gPort.portRect;
savebm = pPrPort->gPort.portBits.bounds;
/*
* Enlarge portRect of Printing Port.
*/
OffsetRect(&pPrPort->gPort.portBits.bounds,-savebm.left,-savebm.top);
scaleRect = (*ourPrint)->prInfoPT.rPage;
OffsetRect(&scaleRect,-scaleRect.left,-scaleRect.top);
pPrPort->gPort.portRect = scaleRect;
RectRgn(pPrPort->gPort.visRgn,&scaleRect);
RectRgn(pPrPort->gPort.clipRgn,&scaleRect);
rows = scaleRect.bottom - scaleRect.top;
if((*ourPrint)->prStl.feed != feedCut || waitnextpage()){
for(i=0;i<rows;i+=vband){ /* Loop over all bands. */
MovePortTo(scaleRect.left,-i); /* Move to next. */
EraseRect(&scaleRect);
DrawPicture(thepic,&scaleRect); /* Draw in the band. */
dumpbits(pPrPort); /* Print it. */
}
formfeed = 12; /* Do a form feed. Device Dependent. */
s->iopb.ioParam.ioReqCount = 1;
s->iopb.ioParam.ioBuffer = &formfeed;
asyncwrite(s,(*((THPrint)pPrPort->PREC))->prJob.pIdleProc);
/*
* Restore portRect so Application can draw in it.
*/
pPrPort->gPort.portBits.bounds = savebm;
pPrPort->gPort.portRect = savepr;
RectRgn(pPrPort->gPort.visRgn,&savepr);
RectRgn(pPrPort->gPort.clipRgn,&savepr);
}else{
PrSetError(iIOAbortErr); /* iPrAbort? */
}
}
KillPicture(thepic);
}
dumpbits(pPrPort) /* Dump a printing band to the serial port. */
TPPrPort pPrPort;
{
DPstorage s;
int i,rows;
BitMap *bits;
ProcPtr idle;
s = (DPstorage)pPrPort->storage;
bits = &pPrPort->gPort.portBits;
rows = bits->bounds.bottom-bits->bounds.top;
idle = (*((THPrint)pPrPort->PREC))->prJob.pIdleProc;
for (i=0;i<rows;i+=16){
bitmap_to_hires(s,bits,i,rows-i,idle);
if(PrError() != noErr)return;
}
}
/*
* bitmap_to_hires()
* This function translates a QuickDraw BitMap to codes which may
* be sent to a Tandy DMP-110 dot-matrix printer in high-resolution
* graphics mode. Graphics codes for this printer in hi-res mode
* include all eight bit characters. There are two characters for each
* column of 16 dots on the paper. The top dot (1) corresponds to bit
* zero of the first byte sent. The bottom dot (16) corresponds to bit
* eight of the second byte. There are 960 columns, or 1920 bytes, of
* graphics data to be sent for one line of graphics output.
* Note that the ToolBox bit manipulation routines use lower-to-upper
* bit order.
*
* No smarts in this routine, the whole 1918 bytes are sent for each
* line.
*/
bitmap_to_hires(s,b,row,nleft,idle)
DPstorage s;
BitMap *b; /* BitMap to print. */
int row; /* Starting row on this pass. */
int nleft; /* Max number of rows to print. */
ProcPtr idle;
{
short *obytes,*inbytes;
int column,lastcol,nbits,dot;
obytes = (short *)s->obuf;
for(dot=960;dot-- >0;){
obytes[dot] = 0;
}
lastcol = b->bounds.right - b->bounds.left;
lastcol = (lastcol > 959) ? 959 : lastcol;
inbytes = (short *)(b->baseAddr + b->rowBytes*row);
nbits = b->rowBytes*8;
for(column=0;column<lastcol;column++){
(*idle)();
if(PrError() != noErr)return;
for(dot=0;dot<8 && dot<nleft;dot++){
if(BitTst(inbytes,(long)(column+dot*nbits))){
BitSet(obytes,(long)(7-dot));
}
}
for(dot=8;dot<16 && dot < nleft;dot++){
if(BitTst(inbytes,(long)(column+dot*nbits))){
BitSet(obytes,(long)(23-dot));
}
}
obytes++;
}
output_hires_data(s,(long)(lastcol),idle); /* Send to printer. */
}
/*
* output_hires_data() - Send a stream of high resolution graphics data
* to the Tandy DMP-110. The codes for transferring the graphics, and
* for the high-resolution paper advance, are hard-coded into this
* routine. Skips blank graphics codes, and repositions the printer
* head. The overhead is 8 bytes for repositioning the head and restarting
* high resolution graphics. If there are four blank columns, then, we
* break even. If there are more, we win. If there are less than four
* blank columns, we lose.
*/
output_hires_data(s,columns,idle)
DPstorage s;
long columns;
ProcPtr idle;
{
unsigned char codes[10];
short *p;
short pos,ncodes,start;
short *buf = (short *)s->obuf;
for(pos=0,ncodes=0,p=buf,start=0;pos<960;pos++){
if(buf[pos] == 0){
if(ncodes != 0){
sendcodes(s,p,ncodes,start,idle);
if(PrError() != noErr)return;
ncodes = 0;
}
}else{
if(ncodes == 0){
p = &buf[pos];
start = pos;
}
ncodes++;
}
}
if(ncodes != 0){
sendcodes(s,p,ncodes,start,idle);
}
codes[0] = 26; /* DMP-110 carriage return. */
codes[1] = 27; /* High res line feed. */
codes[2] = 'G';
s->iopb.ioParam.ioReqCount = 3L;
s->iopb.ioParam.ioBuffer = (Ptr)codes;
asyncwrite(s,idle);
}
sendcodes(s,buf,n,pos,idle)
DPstorage s;
char *buf;
int n,pos;
ProcPtr idle;
{
unsigned char codes[10];
codes[0] = 27;
codes[1] = 16;
codes[2] = (pos>>8)&3;
codes[3] = pos & 0xFF;
codes[4] = 27;
codes[5] = 73;
codes[6] = (n>>8)&3;
codes[7] = n&0xFF;
s->iopb.ioParam.ioReqCount = 8L;
s->iopb.ioParam.ioBuffer = (Ptr)codes;
asyncwrite(s,idle);
s->iopb.ioParam.ioReqCount = (long)(2*n);
s->iopb.ioParam.ioBuffer = buf;
asyncwrite(s,idle);
}
/*
* Modal dialog box: "Insert next sheet."
*/
waitnextpage()
{
DialogPtr sheetdialog;
WindowPtr tempport;
short itemhit,donetype;
Handle doneitem;
Rect donebox;
if((sheetdialog = GetNewDialog(SHEETDIALOG, 0L,(WindowPtr) -1)) == nil)
return FALSE;
InitCursor();
GetDItem(sheetdialog,DONEITEM,&donetype,&doneitem,&donebox);
GetPort(&tempport);
SetPort(sheetdialog);
PenSize(3,3);
InsetRect(&donebox,-4,-4);
FrameRoundRect(&donebox,16,16);
ModalDialog(0L,&itemhit);
DisposDialog(sheetdialog);
SetPort(tempport);
if(itemhit == STOPITEM) return FALSE;
return TRUE;
}
/*
* Open the serial driver and configure it. Quit if ioResult field
* of parameter block ever becomes other than noErr.
*/
OSErr SPOpen(s,World)
register DPstorage s;
SysEnvRec *World;
{
register ParmBlkPtr pb;
int serconfig;
Pfg settings;
OSErr error;
extern char AOutName[],BOutName[];
extern short BaudRates[];
error = noErr;
settings = (Pfg)(GetResource('HEXA',RESFILEID));
if(settings == nil){ /* Where's our stuff? */
return ResError();
}
LoadResource(settings);
HNoPurge(settings);
pb = &s->iopb;
switch (pport){ /* get the correct port */
case 0: /* modem port */
pb->ioParam.ioNamePtr = (StringPtr)AOutName;
break;
case 1: /* printer port */
if(IsMPPOpen()){
(void)StopAlert(ATALKALERT,nil);
return portInUse;
}
pb->ioParam.ioNamePtr = (StringPtr)BOutName;
break;
}
PBOpen(pb,FALSE);
if (pb->ioParam.ioResult != noErr){
return(pb->ioParam.ioResult);
}
pb->ioParam.ioNamePtr = nil;
/*
* Set up the io parameter block for writing to the serial driver.
* a control call resets the baud rate.
* Another sets flow control.
*/
((CntrlParam *)pb)->csCode = SERRESET;
serconfig = data8 + noParity + stop20;
serconfig += BaudRates[pbaud];
((CntrlParam*)pb)->csParam[0] = serconfig;
PBControl(pb,FALSE);
if (pb->ioParam.ioResult != noErr) return(pb->ioParam.ioResult);
#define shake ((SerShk *)&((CntrlParam*)pb)->csParam[0])
shake->errs = FALSE;
shake->evts = FALSE;
shake->fDTR = FALSE;
shake->fInX = FALSE;
if(XonXoff && (World->machineType >= envMachUnknown)){
shake->fXOn = TRUE;
shake->fCTS = FALSE;
shake->xOn = XONCR;
shake->xOff = XOFFCR;
}
else {
shake->fXOn = FALSE;
shake->fCTS = TRUE;
}
((CntrlParam *)pb)->csCode = SERSHAKE;
PBControl(pb,FALSE);
if (pb->ioParam.ioResult != noErr) return(pb->ioParam.ioResult);
pb->ioParam.ioPosMode = 0;
pb->ioParam.ioPosOffset = 0;
ReleaseResource(settings);
return(noErr);
}
/*
* Routine to write asynchronously to the serial port, and run an
* idle procedure while we wait. The idle routine may call PrSetError(),
* so we check PrError() for abort each time. Kill pending serial port
* IO if abort detected.
*/
asyncwrite(s,idle)
DPstorage s;
ProcPtr idle;
{
IOParam *p = &(s->iopb.ioParam);
PBWrite(p,TRUE); /* Issue ASYNC write. */
do{
(*idle)(); /* Idle til done. */
if(PrError() != noErr){ /* Check for abort. */
if(p->ioResult > 0){ /* More chars? */
PBKillIO(p,FALSE); /* Stop output. */
}
longjmp(s->abortbuf,1); /* Get out. */
}
}while(p->ioResult > 0); /* Check for complete. */
if(p->ioResult < 0){ /* Serial port Error! */
longjmp(s->abortbuf,1);
}
}